
#ifndef FIBER_ENV_FIBER_ENV_IMPL_H_
#define FIBER_ENV_FIBER_ENV_IMPL_H_

#include <map>
#include <string>
#include <vector>

#include "./epoll.h"
#include "./fiber.h"
#include "./fiber-env.h"
#include "./fiber-impl.h"
#include "./native-condition.h"
#include "./native-mutex.h"
#include "./queue.h"
#include "./thread.h"
#include "./wait-mgr.h"

namespace fasmio { namespace fiber_env {

class ThreadContext;

class FiberEnvImpl : public FiberEnv
{
    friend class ThreadContext;

public:
    FiberEnvImpl();
    virtual ~FiberEnvImpl();

public:
    virtual bool Start(unsigned int threads);
    virtual bool NotifyToStop();
    virtual bool Stop();

    virtual Fiber* CreateFiber(int (*func)(void*), void* arg, const char* name = nullptr);

    virtual bool FiberizeThisThread();

    virtual void DumpFibers(FILE *fp);

public:
    static void Sleep(unsigned long milliseconds);
    static void Yield();
    static void Quit(int status);
    static void NotifyToStop_s();
    static Fiber* CreateFiber_s(int (*func)(void*), void* arg, const char* name = nullptr);
    static Fiber* SelfFiber();
    static int JoinFiber(FiberImpl* fiber);
    static void DumpFibers_s(FILE *fp);

    // `state' should be a literal string
    static void SetCurrentFiberState(const char* state);
    static void SetCurrentFiberStateFromRunning(const char* state);

    static ThreadContext* GetThreadContext();

public:
    void ReadyFiber(FiberImpl* fiber);
    void RecycleFiberIfQuitted(FiberImpl* fiber);

private:
    static void ThreadProc(void *arg);
    void ThreadProc();

private:
    static void CompleteSleep(ThreadContext*, void*);
    static void CompleteYield(ThreadContext*, void*);
    static void CompleteQuit (ThreadContext*, void*);
    static void CompleteJoin (ThreadContext*, void*);

private:
    bool quit_flag_;

    typedef std::map<unsigned long, FiberImpl*> fibers_t;
    fibers_t live_fibers_;  // maps from fiber-id to fiber itself
    fibers_t quitted_fibers_;  // maps from fiber-id to fiber itself
    fibers_t joining_fibers_;  // maps from target fiber-id to the joining fiber
    NativeMutex fibers_lock_;
    NativeCondition no_live_fibers_cond_;

    typedef Queue<FiberImpl, NativeMutex, NativeCondition> queue_t;
    queue_t ready_queue_;

    std::vector<Thread*> threads_;
    volatile long fiberized_threads_;

    WaitingFiberManager waiting_fiber_manager_;
    Epoller epoller_;
};


class ThreadContext
{
    friend class FiberEnvImpl;

public:
    FiberImpl* GetCurrentFiber();
    bool Schedule(const char* state);
    bool Schedule(void (*func)(ThreadContext*, void*), void *arg, const char* state);
    void ReadyFiber(FiberImpl* fiber);

    WaitingFiberManager* GetWaitingFiberManager();
    Epoller* GetEpoller();

private:
    FiberEnvImpl *env_;
    PlatformFiber *schedule_fiber_;
    FiberImpl *current_fiber_;
    void (*post_switch_action_)(ThreadContext*, void*);
    void *post_switch_arg_;
};


}}  // namespace fasmio::fiber_env

#endif  // FIBER_ENV_FIBER_ENV_IMPL_H_

